home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / strings.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  45KB  |  1,977 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: strings.c,v 4.39 1996/03/15 07:13:42 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.     strings.c
  44.     Misc extra and useful string functions
  45.       - rplstr         replace a substring with another string
  46.       - sqzspaces      Squeeze out the extra blanks in a string
  47.       - sqzsnewlines   Squeeze out \n and \r.
  48.       - remove_trailing_white_space 
  49.       - remove_leading_white_space 
  50.       - strclean       Remove leading and trailing white space and convert
  51.                        to lower case
  52.       - strucmp        A case insensitive strcmp
  53.       - struncmp       A case insensitve strncmp
  54.       - srchstr        Search a string for a sub string
  55.       - strindex       Replacement for strchr/index
  56.       - strrindex      Replacement for strrchr/rindex
  57.       - sstrcpy        Copy one string onto another, advancing dest'n pointer
  58.       - month_abbrev   Return three letter abbreviations for months
  59.       - month_num      Calculate month number from month/year string
  60.       - cannon_date    Formalize format of a some what formatted date
  61.       - pretty_command Return nice string describing character
  62.       - blanks         Returns a string n blanks long
  63.       - comatose       Format number with nice commas
  64.       - byte_string    Format number of bytes with Kb, Mb, Gb or bytes
  65.       - enth-string    Format number i.e. 1: 1st, 983: 983rd....
  66.       - read_hex       Convert 1 or 2-digit hex string to integer
  67.       - string_to_cstring  Convert string to C-style constant string with \'s
  68.       - cstring_to_hexstring  Convert cstring to hex string
  69.  
  70.  ====*/
  71.  
  72. #include "headers.h"
  73.  
  74. void char_to_hex_pair PROTO((int, char *));
  75. void char_to_octal_triple PROTO((int, char *));
  76. int  read_octal PROTO((char *));
  77.  
  78.  
  79. /*----------------------------------------------------------------------
  80.        Replace n characters in one string with another given string
  81.  
  82.    args: os -- the output string
  83.          dl -- the number of character to delete from start of os
  84.          is -- The string to insert
  85.   
  86.  Result: returns pointer in originl string to end of string just inserted
  87.          First 
  88.   ---*/
  89. char *
  90. rplstr(os,dl,is)
  91. char *os,*is;
  92. int dl;
  93. {   
  94.     register char *x1,*x2,*x3;
  95.     int           diff;
  96.  
  97.     if(os == NULL)
  98.         return(NULL);
  99.        
  100.     for(x1 = os; *x1; x1++);
  101.     if(dl > x1 - os)
  102.         dl = x1 - os;
  103.         
  104.     x2 = is;      
  105.     if(is != NULL){
  106.         while(*x2++);
  107.         x2--;
  108.     }
  109.  
  110.     if((diff = (x2 - is) - dl) < 0){
  111.         x3 = os; /* String shrinks */
  112.         if(is != NULL)
  113.             for(x2 = is; *x2; *x3++ = *x2++); /* copy new string in */
  114.         for(x2 = x3 - diff; *x2; *x3++ = *x2++); /* shift for delete */
  115.         *x3 = *x2;
  116.     } else {                
  117.         /* String grows */
  118.         for(x3 = x1 + diff; x3 >= os + (x2 - is); *x3-- = *x1--); /* shift*/
  119.         for(x1 = os, x2 = is; *x2 ; *x1++ = *x2++);
  120.         while(*x3) x3++;                 
  121.     }
  122.     return(x3);
  123. }
  124.  
  125.  
  126.  
  127. /*----------------------------------------------------------------------
  128.      Squeeze out blanks 
  129.   ----------------------------------------------------------------------*/
  130. void
  131. sqzspaces(string)
  132.      char *string;
  133. {
  134.     char *p = string;
  135.  
  136.     while(*string = *p++)        /* while something to copy       */
  137.       if(!isspace(*string))        /* only really copy if non-blank */
  138.     string++;
  139. }
  140.  
  141.  
  142.  
  143. /*----------------------------------------------------------------------
  144.      Squeeze out CR's and LF's 
  145.   ----------------------------------------------------------------------*/
  146. void
  147. sqznewlines(string)
  148.     char *string;
  149. {
  150.     char *p = string;
  151.  
  152.     while(*string = *p++)              /* while something to copy  */
  153.       if(*string != '\r' && *string != '\n')  /* only copy if non-newline */
  154.     string++;
  155. }
  156.  
  157.  
  158.  
  159. /*----------------------------------------------------------------------  
  160.        Remove trailing white space from a string in place
  161.   
  162.   Args: string -- string to remove space from
  163.   ----*/
  164. void
  165. removing_leading_white_space(string)
  166.      char *string;
  167. {
  168.     char *p;
  169.  
  170.     /* ignore the null/non-blank string */
  171.     if(*(p = string) && isspace((unsigned char)*p)){
  172.     /* find the first non-blank  */
  173.     while(*p && isspace((unsigned char)*p))
  174.       p++;
  175.  
  176.     while(*string++ = *p++)        /* copy from there... */
  177.       ;
  178.     }
  179. }
  180.  
  181.  
  182.  
  183. /*----------------------------------------------------------------------  
  184.        Remove trailing white space from a string in place
  185.   
  186.   Args: string -- string to remove space from
  187.   ----*/
  188. void
  189. removing_trailing_white_space(string)
  190.      char *string;
  191. {
  192.     char *p = NULL;
  193.  
  194.     for(; *string; string++)        /* remember start of whitespace */
  195.       p = (!isspace((unsigned char)*string)) ? NULL : (!p) ? string : p;
  196.  
  197.     if(p)                /* if whitespace, blast it */
  198.       *p = '\0';
  199. }
  200.  
  201.  
  202.  
  203. /*----------------------------------------------------------------------  
  204.        Remove quotes from a string in place
  205.   
  206.   Args: string -- string to remove quotes from
  207.   Rreturns: string passed us, but with quotes gone
  208.   ----*/
  209. char *
  210. removing_quotes(string)
  211.     char *string;
  212. {
  213.     register char *p, *q;
  214.  
  215.     if(*(p = q = string) == '\"'){
  216.     do
  217.       if(*q == '\"' || *q == '\\')
  218.         q++;
  219.     while(*p++ = *q++);
  220.     }
  221.  
  222.     return(string);
  223. }
  224.  
  225.  
  226.  
  227. /*--------------------------------------------------
  228.      A case insensitive strcmp()     
  229.   
  230.    Args: o, r -- The two strings to compare
  231.  
  232.  Result: integer indicating which is greater
  233.   ---*/
  234. strucmp(o, r)
  235.     register char *o, *r;
  236. {
  237.     if(o == NULL){
  238.     if(r == NULL)
  239.       return 0;
  240.     else
  241.       return -1;
  242.     }
  243.     else if(r == NULL)
  244.       return 1;
  245.  
  246.     while(*o && *r && (isupper(*o) ? tolower(*o) : *o) ==
  247.                   (isupper(*r) ? tolower(*r) : *r)){
  248.     o++;
  249.     r++;
  250.     }
  251.  
  252.     return((isupper(*o) ? tolower(*o): *o)-(isupper(*r) ? tolower(*r) : *r));
  253. }
  254.  
  255.  
  256. /*---------------------------------------------------
  257.      Remove leading whitespace, trailing whitespace and convert 
  258.      to lowercase
  259.  
  260.    Args: s, -- The string to clean
  261.  
  262.  Result: the cleaned string
  263.   ----*/
  264. char *
  265. strclean(string)
  266.      char *string;
  267. {
  268.     char *s = string, *sc = NULL, *p = NULL;
  269.  
  270.     for(; *s; s++){                /* single pass */
  271.     if(!isspace((unsigned char) *s)){
  272.         p = NULL;                /* not start of blanks   */
  273.         if(!sc)                /* first non-blank? */
  274.           sc = string;            /* start copying */
  275.     }
  276.     else if(!p)                /* it's OK if sc == NULL */
  277.       p = sc;                /* start of blanks? */
  278.  
  279.     if(sc)                    /* if copying, copy */
  280.       *sc++ = isupper(*s) ? tolower(*s) : *s;
  281.     }
  282.  
  283.     if(p)                    /* if ending blanks  */
  284.       *p = '\0';                /* tie off beginning */
  285.     else if(!sc)                /* never saw a non-blank */
  286.       *string = '\0';                /* so tie whole thing off */
  287.  
  288.     return(string);
  289. }
  290.  
  291.  
  292.  
  293. /*----------------------------------------------------------------------
  294.      A case insensitive strncmp()     
  295.   
  296.    Args: o, r -- The two strings to compare
  297.          n    -- length to stop comparing strings at
  298.  
  299.  Result: integer indicating which is greater
  300.    
  301.   ----*/
  302. struncmp(o, r, n)
  303.     register char *o,
  304.           *r;
  305.     register int   n;
  306. {
  307.     if(o == NULL){
  308.     if(r == NULL)
  309.       return 0;
  310.     else
  311.       return -1;
  312.     }
  313.     else if(r == NULL)
  314.       return 1;
  315.  
  316.     n--;
  317.     while(n && *o && *r &&
  318.           (isupper(*o)? tolower(*o): *o) == (isupper(*r)? tolower(*r): *r)){
  319.     o++;
  320.     r++;
  321.     n--;
  322.     }
  323.  
  324.     return((isupper(*o)? tolower(*o): *o) - (isupper(*r)? tolower(*r): *r));
  325. }
  326.  
  327.  
  328.  
  329. /*----------------------------------------------------------------------
  330.         Search one string for another
  331.  
  332.    Args:  is -- The string to search in, the larger string
  333.           ss -- The string to search for, the smaller string
  334.  
  335.    Search for any occurance of ss in the is, and return a pointer
  336.    into the string is when it is found. The search is case indepedent.
  337.   ----*/
  338.  
  339. char *        
  340. srchstr(is,ss)
  341. register char *is,*ss;
  342. {                    
  343.     register char *sx,*sy, *ss_store;
  344.     char          *ss_store_nr, *rv;
  345.     char          temp[251];
  346.     
  347.     if(is == NULL || ss == NULL)
  348.         return(NULL);
  349.  
  350.     if(strlen(ss) > sizeof(temp) - 2)
  351.       ss_store = fs_get(strlen(ss) + 1);
  352.     else
  353.       ss_store = temp;
  354.     for(sx = ss, sy = ss_store; *sx != '\0' ; sx++, sy++)
  355.       *sy = isupper(*sx) ? tolower(*sx) : *sx;
  356.     *sy = *sx;
  357.  
  358.     rv = NULL;
  359.     while(*is != '\0'){
  360.         for(sx = is, sy = ss_store; ((*sx == *sy) ||
  361.                   ((isupper(*sx) ? tolower(*sx): *sx) == *sy)) && *sy;
  362.                                                             sx++, sy++);
  363.         if(!*sy) {
  364.             rv = is;
  365.             break;
  366.         }
  367.         is++;
  368.     }
  369.     if(ss_store != temp) {
  370.         ss_store_nr = ss_store;
  371.         fs_give((void **)&ss_store_nr);
  372.     }
  373.     return(rv);
  374. }
  375.  
  376.  
  377.  
  378. /*----------------------------------------------------------------------
  379.     A replacement for strchr or index ...
  380.  
  381.     Returns a pointer to the first occurance of the character
  382.     'ch' in the specified string or NULL if it doesn't occur
  383.  
  384.  ....so we don't have to worry if it's there or not. We bring our own.
  385. If we really care about efficiency and think the local one is more
  386. efficient the local one can be used, but most of the things that take
  387. a long time are in the c-client and not in pine.
  388.  ----*/
  389. char *
  390. strindex(buffer, ch)
  391.     char *buffer;
  392.     int ch;
  393. {
  394.     do
  395.       if(*buffer == ch)
  396.     return(buffer);
  397.     while (*buffer++ != '\0');
  398.  
  399.     return(NULL);
  400. }
  401.  
  402.  
  403. /* Returns a pointer to the last occurance of the character
  404.  * 'ch' in the specified string or NULL if it doesn't occur
  405.  */
  406. char *
  407. strrindex(buffer, ch)
  408.     char *buffer;
  409.     int   ch;
  410. {
  411.     char *address = NULL;
  412.  
  413.     do
  414.       if(*buffer == ch)
  415.     address = buffer;
  416.     while (*buffer++ != '\0');
  417.     return(address);
  418. }
  419.  
  420.  
  421.  
  422. /*----------------------------------------------------------------------
  423.   copy the source string onto the destination string returning with
  424.   the destination string pointer at the end of the destination text
  425.  
  426.   motivation for this is to avoid twice passing over a string that's
  427.   being appended to twice (i.e., strcpy(t, x); t += strlen(t))
  428.  ----*/
  429. void
  430. sstrcpy(d, s)
  431.     char **d;
  432.     char *s;
  433. {
  434.     while((**d = *s++) != '\0')
  435.       (*d)++;
  436. }
  437.  
  438.  
  439. char *xdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
  440.  
  441. char *
  442. month_abbrev(month_num)
  443.      int month_num;
  444. {
  445.     static char *xmonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  446.         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
  447.     if(month_num < 1 || month_num > 12)
  448.       return("xxx");
  449.     return(xmonths[month_num - 1]);
  450. }
  451.  
  452. char *
  453. week_abbrev(week_day)
  454.      int week_day;
  455. {
  456.     return(xdays[week_day]);
  457. }
  458.  
  459.  
  460. days_in_month(month, year)
  461.      int month, year;
  462. {
  463.     static int d_i_m[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
  464.  
  465.     if(month == 2) 
  466.       return(((year%4) == 0 && (year%100) != 0) ? 29 : 28);
  467.     else
  468.       return(d_i_m[month]);
  469. }
  470.     
  471.  
  472.  
  473. /*----------------------------------------------------------------------
  474.       Return month number of month named in string
  475.   
  476.    Args: s -- string with 3 letter month abbreviation of form mmm-yyyy
  477.  
  478.  Result: Returns month number with January, year 1900, 2000... being 0;
  479.          -1 if no month/year is matched
  480.  ----*/
  481. int
  482. month_num(s)
  483.      char *s;
  484. {
  485.     int month, year;
  486.     int i;
  487.  
  488.     for(i = 0; i < 12; i++){
  489.         if(struncmp(month_abbrev(i+1), s, 3) == 0)
  490.           break;
  491.     }
  492.     if(i == 12)
  493.       return(-1);
  494.  
  495.     year = atoi(s + 4);
  496.     if(year == 0)
  497.       return(-1);
  498.  
  499.     month = (year < 100 ? year + 1900 : year)  * 12 + i;
  500.     return(month);
  501. }
  502.  
  503.  
  504. /*
  505.  * Structure containing all knowledge of symbolic time zones.
  506.  * To add support for a given time zone, add it here, but make sure
  507.  * the zone name is in upper case.
  508.  */
  509. static struct {
  510.     char  *zone;
  511.     short  len,
  512.            hour_offset,
  513.        min_offset;
  514. } known_zones[] = {
  515.     {"PST", 3, -8, 0},            /* Pacific Standard */
  516.     {"PDT", 3, -7, 0},            /* Pacific Daylight */
  517.     {"MST", 3, -7, 0},            /* Mountain Standard */
  518.     {"MDT", 3, -6, 0},            /* Mountain Daylight */
  519.     {"CST", 3, -6, 0},            /* Central Standard */
  520.     {"CDT", 3, -5, 0},            /* Central Daylight */
  521.     {"EST", 3, -5, 0},            /* Eastern Standard */
  522.     {"EDT", 3, -4, 0},            /* Eastern Daylight */
  523.     {"JST", 3,  9, 0},            /* Japan Standard */
  524.     {"GMT", 3,  0, 0},            /* Universal Time */
  525.     {"UT",  2,  0, 0},            /* Universal Time */
  526. #ifdef    IST_MEANS_ISREAL
  527.     {"IST", 3,  2, 0},            /* Israel Standard */
  528. #else
  529. #ifdef    IST_MEANS_INDIA
  530.     {"IST", 3,  5, 30},            /* India Standard */
  531. #endif
  532. #endif
  533.     {NULL, 0, 0},
  534. };
  535.  
  536. /*----------------------------------------------------------------------
  537.   Parse date in or near RFC-822 format into the date structure
  538.  
  539. Args: given_date -- The input string to parse
  540.       d          -- Pointer to a struct date to place the result in
  541.  
  542. Returns nothing
  543.  
  544. The following date fomrats are accepted:
  545.   WKDAY DD MM YY HH:MM:SS ZZ
  546.   DD MM YY HH:MM:SS ZZ
  547.   WKDAY DD MM HH:MM:SS YY ZZ
  548.   DD MM HH:MM:SS YY ZZ
  549.   DD MM WKDAY HH:MM:SS YY ZZ
  550.   DD MM WKDAY YY MM HH:MM:SS ZZ
  551.  
  552. All leading, intervening and trailing spaces tabs and commas are ignored.
  553. The prefered formats are the first or second ones.  If a field is unparsable
  554. it's value is left as -1. 
  555.  
  556.   ----*/
  557. void
  558. parse_date(given_date, d)
  559.      char        *given_date;
  560.      struct date *d;
  561. {
  562.     char *p, **i, *q, n;
  563.     int   month;
  564.  
  565.     d->sec   = -1;
  566.     d->minute= -1;
  567.     d->hour  = -1;
  568.     d->day   = -1;
  569.     d->month = -1;
  570.     d->year  = -1;
  571.     d->wkday = -1;
  572.     d->hours_off_gmt = -1;
  573.     d->min_off_gmt   = -1;
  574.  
  575.     if(given_date == NULL)
  576.       return;
  577.  
  578.     p = given_date;
  579.     while(*p && isspace(*p))
  580.       p++;
  581.  
  582.     /* Start with month, weekday or day ? */
  583.     for(i = xdays; *i != NULL; i++) 
  584.       if(struncmp(p, *i, 3) == 0) /* Match first 3 letters */
  585.         break;
  586.     if(*i != NULL) {
  587.         /* Started with week day */
  588.         d->wkday = i - xdays;
  589.         while(*p && !isspace(*p) && *p != ',')
  590.           p++;
  591.         while(*p && (isspace(*p) || *p == ','))
  592.           p++;
  593.     }
  594.     if(isdigit(*p)) {
  595.         d->day = atoi(p);
  596.         while(*p && isdigit(*p))
  597.           p++;
  598.         while(*p && (*p == '-' || *p == ',' || isspace(*p)))
  599.           p++;
  600.     }
  601.     for(month = 1; month <= 12; month++)
  602.       if(struncmp(p, month_abbrev(month), 3) == 0)
  603.         break;
  604.     if(month < 13) {
  605.         d->month = month;
  606.  
  607.     } 
  608.     /* Move over month, (or whatever is there) */
  609.     while(*p && !isspace(*p) && *p != ',' && *p != '-')
  610.        p++;
  611.     while(*p && (isspace(*p) || *p == ',' || *p == '-'))
  612.        p++;
  613.  
  614.     /* Check again for day */
  615.     if(isdigit(*p) && d->day == -1) {
  616.         d->day = atoi(p);
  617.         while(*p && isdigit(*p))
  618.           p++;
  619.         while(*p && (*p == '-' || *p == ',' || isspace(*p)))
  620.           p++;
  621.     }
  622.  
  623.     /*-- Check for time --*/
  624.     for(q = p; *q && isdigit(*q); q++);
  625.     if(*q == ':') {
  626.         /* It's the time (out of place) */
  627.         d->hour = atoi(p);
  628.         while(*p && *p != ':' && !isspace(*p))
  629.           p++;
  630.         if(*p == ':') {
  631.             p++;
  632.             d->minute = atoi(p);
  633.             while(*p && *p != ':' && !isspace(*p))
  634.               p++;
  635.             if(*p == ':') {
  636.                 d->sec = atoi(p);
  637.                 while(*p && !isspace(*p))
  638.                   p++;
  639.             }
  640.         }
  641.         while(*p && isspace(*p))
  642.           p++;
  643.     }
  644.     
  645.  
  646.     /* Get the year 0-49 is 2000-2049; 50-100 is 1950-1999 and
  647.                                            101-9999 is 101-9999 */
  648.     if(isdigit(*p)) {
  649.         d->year = atoi(p);
  650.         if(d->year < 50)   
  651.           d->year += 2000;
  652.         else if(d->year < 100)
  653.           d->year += 1900;
  654.         while(*p && isdigit(*p))
  655.           p++;
  656.         while(*p && (*p == '-' || *p == ',' || isspace(*p)))
  657.           p++;
  658.     } else {
  659.         /* Something wierd, skip it and try to resynch */
  660.         while(*p && !isspace(*p) && *p != ',' && *p != '-')
  661.           p++;
  662.         while(*p && (isspace(*p) || *p == ',' || *p == '-'))
  663.           p++;
  664.     }
  665.  
  666.     /*-- Now get hours minutes, seconds and ignore tenths --*/
  667.     for(q = p; *q && isdigit(*q); q++);
  668.     if(*q == ':' && d->hour == -1) {
  669.         d->hour = atoi(p);
  670.         while(*p && *p != ':' && !isspace(*p))
  671.           p++;
  672.         if(*p == ':') {
  673.             p++;
  674.             d->minute = atoi(p);
  675.             while(*p && *p != ':' && !isspace(*p))
  676.               p++;
  677.             if(*p == ':') {
  678.                 p++;
  679.                 d->sec = atoi(p);
  680.                 while(*p && !isspace(*p))
  681.                   p++;
  682.             }
  683.         }
  684.     }
  685.     while(*p && isspace(*p))
  686.       p++;
  687.  
  688.  
  689.     /*-- The time zone --*/
  690.     d->hours_off_gmt = 0;
  691.     d->min_off_gmt = 0;
  692.     if(*p) {
  693.         if((*p == '+' || *p == '-')
  694.        && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3]) && isdigit(p[4])
  695.        && !isdigit(p[5])) {
  696.             char tmp[3];
  697.             d->min_off_gmt = d->hours_off_gmt = (*p == '+' ? 1 : -1);
  698.             p++;
  699.             tmp[0] = *p++;
  700.             tmp[1] = *p++;
  701.             tmp[2] = '\0';
  702.             d->hours_off_gmt *= atoi(tmp);
  703.             tmp[0] = *p++;
  704.             tmp[1] = *p++;
  705.             tmp[2] = '\0';
  706.             d->min_off_gmt *= atoi(tmp);
  707.         } else {
  708.         for(n = 0; known_zones[n].zone; n++)
  709.           if(struncmp(p, known_zones[n].zone, known_zones[n].len) == 0){
  710.           d->hours_off_gmt = (int) known_zones[n].hour_offset;
  711.           d->min_off_gmt   = (int) known_zones[n].min_offset;
  712.           break;
  713.           }
  714.         }
  715.     }
  716.     dprint(9, (debugfile,
  717.      "Parse date: \"%s\" to..  hours_off_gmt:%d  min_off_gmt:%d\n",
  718.                given_date, d->hours_off_gmt, d->min_off_gmt));
  719.     dprint(9, (debugfile,
  720.            "Parse date: wkday:%d  month:%d  year:%d  day:%d  hour:%d  min:%d  sec:%d\n",
  721.             d->wkday, d->month, d->year, d->day, d->hour, d->minute, d->sec));
  722. }
  723.  
  724.  
  725.  
  726. /*----------------------------------------------------------------------
  727.     Convert the given date to GMT
  728.  
  729.   Args -- d:  The date to be converted in place
  730.  ----*/
  731. void
  732. convert_to_gmt(d)
  733.     MESSAGECACHE *d;
  734. {
  735.     int minutes, /* 0-59 */
  736.         hours,   /* 0-23 */
  737.     day,     /* 1-31 */
  738.     month,   /* 1-12 */
  739.     year;    /* since 1969 */
  740.  
  741.     if(d->zhours == 0 && d->zminutes == 0)
  742.       return;
  743.  
  744.     minutes = d->minutes + (d->zoccident ? d->zminutes : (-1 * d->zminutes));
  745.     hours   = d->hours + (d->zoccident ? d->zhours : (-1 * d->zhours));
  746.     day     = d->day;
  747.     month   = d->month;
  748.     year    = d->year;
  749.  
  750.     if(minutes < 0){  /* go to previous hour */
  751.     hours--;
  752.     minutes += 60;
  753.     }
  754.     else if(minutes > 59){  /* next hour */
  755.     hours++;
  756.     minutes -= 60;
  757.     }
  758.  
  759.     if(hours < 0){  /* previous day */
  760.     day--;
  761.     hours += 24;
  762.     }
  763.     else if(hours > 23){  /* next day */
  764.     day++;
  765.     hours -= 24;
  766.     }
  767.  
  768.     if(day < 0){  /* previous month */
  769.     if(--month <= 0){
  770.         month = 12;
  771.         year--;
  772.     }
  773.     day = days_in_month(month);
  774.     }
  775.     else if(day > days_in_month(month)){  /* next month */
  776.     if(++month > 12){
  777.         month = 0;
  778.         year++;
  779.     }
  780.     day = 1;
  781.     }
  782.  
  783.     d->zoccident = 0;
  784.     d->zhours    = 0;
  785.     d->zminutes  = 0;
  786.     d->year      = year;
  787.     d->month     = month;
  788.     d->day       = day;
  789.     d->hours     = hours;
  790.     d->minutes   = minutes;
  791. }
  792.  
  793.  
  794.  
  795. /*----------------------------------------------------------------------
  796.   The arguments are pointers to c-client MESSAGECACHE elts which have
  797.   had dates placed into them.
  798.   ----*/
  799. compare_dates(d1, d2)
  800.     MESSAGECACHE *d1, *d2;
  801. {
  802.     /*
  803.      * Check to see if the two dates are in different timezones, and
  804.      * convert to gmt if they are.
  805.      */
  806.     if(!(d1->zoccident == d2->zoccident &&
  807.          d1->zhours    == d2->zhours    &&
  808.          d1->zminutes  == d2->zminutes)){
  809.     convert_to_gmt(d1);
  810.     convert_to_gmt(d2);
  811.     }
  812.  
  813.     /* Now do the compare */
  814.     if(d1->year == d2->year)
  815.       if(d1->month == d2->month)
  816.         if(d1->day == d2->day)
  817.           if(d1->hours == d2->hours)
  818.             if(d1->minutes == d2->minutes)
  819.               if(d1->seconds == d2->seconds)
  820.         return 0;
  821.           else
  822.                 return((int)(d1->seconds - d2->seconds));
  823.         else
  824.               return((int)(d1->minutes - d2->minutes));
  825.       else
  826.             return((int)(d1->hours - d2->hours));
  827.     else
  828.           return((int)(d1->day - d2->day));
  829.       else
  830.         return((int)(d1->month - d2->month));
  831.     else
  832.       return((int)(d1->year - d2->year));
  833. }
  834.     
  835.  
  836. /*----------------------------------------------------------------------
  837.      Map some of the special characters into sensible strings for human
  838.    consumption.
  839.   ----*/
  840. char *
  841. pretty_command(c)
  842.      int c;
  843. {
  844.     static char  buf[10];
  845.     char    *s;
  846.  
  847.     switch(c){
  848.       case '\033'    : s = "ESC";        break;
  849.       case '\177'    : s = "DEL";        break;
  850.       case ctrl('I') : s = "TAB";        break;
  851.       case ctrl('J') : s = "LINEFEED";        break;
  852.       case ctrl('M') : s = "RETURN";        break;
  853.       case ctrl('Q') : s = "XON";        break;
  854.       case ctrl('S') : s = "XOFF";        break;
  855.       case KEY_UP    : s = "Up Arrow";        break;
  856.       case KEY_DOWN  : s = "Down Arrow";    break;
  857.       case KEY_RIGHT : s = "Right Arrow";    break;
  858.       case KEY_LEFT  : s = "Left Arrow";    break;
  859.       case KEY_PGUP  : s = "Prev Page";        break;
  860.       case KEY_PGDN  : s = "Next Page";        break;
  861.       case KEY_HOME  : s = "Home";        break;
  862.       case KEY_END   : s = "End";        break;
  863.       case KEY_DEL   : s = "Delete";        break; /* Not necessary DEL! */
  864.       case PF1         :
  865.       case PF2         :
  866.       case PF3         :
  867.       case PF4         :
  868.       case PF5         :
  869.       case PF6         :
  870.       case PF7         :
  871.       case PF8         :
  872.       case PF9         :
  873.       case PF10         :
  874.       case PF11         :
  875.       case PF12         :
  876.         sprintf(s = buf, "F%d", c - PF1 + 1);
  877.     break;
  878.  
  879.       default:
  880.     if(c < ' ')
  881.       sprintf(s = buf, "^%c", c + 'A' - 1);
  882.     else
  883.       sprintf(s = buf, "%c", c);
  884.  
  885.     break;
  886.     }
  887.  
  888.     return(s);
  889. }
  890.         
  891.     
  892.  
  893. /*----------------------------------------------------------------------
  894.      Create a little string of blanks of the specified length.
  895.    Max n is 511.
  896.   ----*/
  897. char *
  898. repeat_char(n, c)
  899.      int  n;
  900.      int  c;
  901. {
  902.     static char bb[512];
  903.     if(n > sizeof(bb))
  904.        n = sizeof(bb) - 1;
  905.     bb[n--] = '\0';
  906.     while(n >= 0)
  907.       bb[n--] = c;
  908.     return(bb);
  909. }
  910.  
  911.  
  912. /*----------------------------------------------------------------------
  913.         Turn a number into a string with comma's
  914.  
  915.    Args: number -- The long to be turned into a string. 
  916.  
  917.   Result: pointer to static string representing number with commas
  918.   ---*/
  919. char *
  920. comatose(number) 
  921.     long number;
  922. {
  923. #ifdef    DOS
  924.     static char buf[16];        /* no numbers > 1 trillion! */
  925.     char *b;
  926.     short i;
  927.  
  928.     if(!number)
  929.     return("0");
  930.  
  931.     if(number < 0x7FFFFFFFL){        /* largest DOS signed long */
  932.         buf[15] = '\0';
  933.         b = &buf[14];
  934.         i = 2;
  935.     while(number){
  936.          *b-- = (number%10) + '0';
  937.         if((number /= 10) && i-- == 0 ){
  938.         *b-- = ',';
  939.         i = 2;
  940.         }
  941.     }
  942.     }
  943.     else
  944.       return("Number too big!");        /* just fits! */
  945.  
  946.     return(++b);
  947. #else
  948.     long        i, x, done_one;
  949.     static char buf[100];
  950.     char       *b;
  951.  
  952.     dprint(9, (debugfile, "comatose(%ld) returns:", number));
  953.     if(number == 0){
  954.         strcpy(buf, "0");
  955.         return(buf);
  956.     }
  957.     
  958.     done_one = 0;
  959.     b = buf;
  960.     for(i = 1000000000; i >= 1; i /= 1000) {
  961.     x = number / i;
  962.     number = number % i;
  963.     if(x != 0 || done_one) {
  964.         if(b != buf)
  965.           *b++ = ',';
  966.         sprintf(b, done_one ? "%03ld" : "%d", x);
  967.         b += strlen(b);
  968.         done_one = 1;
  969.     }
  970.     }
  971.     *b = '\0';
  972.  
  973.     dprint(9, (debugfile, "\"%s\"\n", buf));
  974.  
  975.     return(buf);
  976. #endif    /* DOS */
  977. }
  978.  
  979.  
  980.  
  981. /*----------------------------------------------------------------------
  982.    Format number as amount of bytes, appending Kb, Mb, Gb, bytes
  983.  
  984.   Args: bytes -- number of bytes to format
  985.  
  986.  Returns pointer to static string. The numbers are divided to produce a 
  987. nice string with precision of about 2-4 digits
  988.     ----*/
  989. char *
  990. byte_string(bytes)
  991.      long bytes;
  992. {
  993.     char       *a, aa[5];
  994.     char       *abbrevs = "GMK";
  995.     long        i, ones, tenths;
  996.     static char string[10];
  997.  
  998.     ones   = 0L;
  999.     tenths = 0L;
  1000.  
  1001.     if(bytes == 0L){
  1002.         strcpy(string, "0 bytes");
  1003.     } else {
  1004.         for(a = abbrevs, i = 1000000000; i >= 1; i /= 1000, a++) {
  1005.             if(bytes > i) {
  1006.                 ones = bytes/i;
  1007.                 if(ones < 10L && i > 10L)
  1008.                   tenths = (bytes - (ones * i)) / (i / 10L);
  1009.                 break;
  1010.             }
  1011.         }
  1012.     
  1013.         aa[0] = *a;  aa[1] = '\0'; 
  1014.     
  1015.         if(tenths == 0)
  1016.           sprintf(string, "%ld%s%s", ones, aa, *a ? "B" : "bytes");
  1017.         else
  1018.           sprintf(string, "%ld.%ld%s%s", ones, tenths, aa, *a ? "B" : "bytes");
  1019.     }
  1020.  
  1021.     return(string);
  1022. }
  1023.  
  1024.  
  1025.  
  1026. /*----------------------------------------------------------------------
  1027.     Print a string corresponding to the number given:
  1028.       1st, 2nd, 3rd, 105th, 92342nd....
  1029.  ----*/
  1030.  
  1031. char *
  1032. enth_string(i)
  1033.      int i;
  1034. {
  1035.     static char enth[10];
  1036.  
  1037.     switch (i % 10) {
  1038.         
  1039.       case 1:
  1040.         if( (i % 100 ) == 11)
  1041.           sprintf(enth,"%dth", i);
  1042.         else
  1043.           sprintf(enth,"%dst", i);
  1044.         break;
  1045.  
  1046.       case 2:
  1047.         if ((i % 100) == 12)
  1048.           sprintf(enth, "%dth",i);
  1049.         else
  1050.           sprintf(enth, "%dnd",i);
  1051.         break;
  1052.  
  1053.       case 3:
  1054.         if(( i % 100) == 13)
  1055.           sprintf(enth, "%dth",i);
  1056.         else
  1057.           sprintf(enth, "%drd",i);
  1058.         break;
  1059.  
  1060.       default:
  1061.         sprintf(enth,"%dth",i);
  1062.         break;
  1063.     }
  1064.     return(enth);
  1065. }
  1066.  
  1067.  
  1068. char *
  1069. long2string(l)
  1070.      long l;
  1071. {
  1072.     static char string[20];
  1073.     sprintf(string, "%ld", l);
  1074.     return(string);
  1075. }
  1076.  
  1077. char *
  1078. int2string(i)
  1079.      int i;
  1080. {
  1081.     static char string[20];
  1082.     sprintf(string, "%d", i);
  1083.     return(string);
  1084. }
  1085.  
  1086.  
  1087. /*
  1088.  *  Function to parse the given string into two space-delimited fields
  1089.  */
  1090. void
  1091. get_pair(string, label, value, firstws)
  1092.     char *string, **label, **value;
  1093.     int   firstws;
  1094. {
  1095.     char *p, *token = NULL;
  1096.     int      quoted = 0;
  1097.  
  1098.     *label = *value = NULL;
  1099.     for(p = string; *p;){
  1100.     if(*p == '"')                /* quoted label? */
  1101.       quoted = (quoted) ? 0 : 1;
  1102.  
  1103.     if(*p == '\\' && *(p+1) == '"')        /* escaped quote? */
  1104.       p++;                    /* skip it... */
  1105.  
  1106.     if(isspace(*p) && !quoted){        /* if space,  */
  1107.         while(*++p && isspace(*p))        /* move past it */
  1108.           ;
  1109.  
  1110.         if(!firstws || !token)
  1111.           token = p;            /* remember start of text */
  1112.     }
  1113.     else
  1114.       p++;
  1115.     }
  1116.  
  1117.     if(token){
  1118.     *label = p = (char *)fs_get(((token - string) + 1) * sizeof(char));
  1119.     for(; string < token ; string++){
  1120.         if(*string == '\\' && *(string+1) == '"')
  1121.           *p++ = *++string;
  1122.         else if(*string != '"')
  1123.           *p++ = *string;
  1124.     }
  1125.  
  1126.     *p = '\0';                /* tie off nickname */
  1127.     removing_trailing_white_space(*label);
  1128.     *value = cpystr(token);
  1129.     }
  1130.     else
  1131.       *value = cpystr(string);
  1132. }
  1133.  
  1134.  
  1135. /*
  1136.  * Convert a 1 or 2-digit hex string into an 8-bit character.
  1137.  * The input string should be checked for hex'ness before calling this.
  1138.  * Only the first two characters of s will be used, and it is ok not
  1139.  * to null-terminate it.
  1140.  */
  1141. int
  1142. read_hex(s)
  1143.     char *s;
  1144. {
  1145.     register int i, c, j;
  1146.  
  1147.     i = 0;
  1148.     for(j = 0; j < 2 && *s && isxdigit(*s); s++,j++){
  1149.     c = *s;
  1150.     if(isupper(c))
  1151.       c = tolower(c);
  1152.  
  1153.     i = i*16 + (isdigit(c)  ? c - '0'
  1154.                 : c - 'a' + 10);
  1155.     }
  1156.  
  1157.     return(i);
  1158. }
  1159.  
  1160.  
  1161. /*
  1162.  * Convert a 1, 2, or 3-digit octal string into an 8-bit character.
  1163.  * Only the first three characters of s will be used, and it is ok not
  1164.  * to null-terminate it.
  1165.  */
  1166. int
  1167. read_octal(s)
  1168.     char *s;
  1169. {
  1170.     register int i, c, j;
  1171.  
  1172.     i = 0;
  1173.     for(j = 0; j < 3 && *s && isdigit(*s); s++,j++)
  1174.       i = i*8 + *s - '0';
  1175.  
  1176.     return(i);
  1177. }
  1178.  
  1179.  
  1180. /*
  1181.  * Given a character c, put the 3-digit ascii octal value of that char
  1182.  * in the 2nd argument, which must be at least 3 in length.
  1183.  */
  1184. void
  1185. char_to_octal_triple(c, octal)
  1186.     int   c;
  1187.     char *octal;
  1188. {
  1189.     c &= 0xff;
  1190.  
  1191.     octal[2] = (c % 8) + '0';
  1192.     c /= 8;
  1193.     octal[1] = (c % 8) + '0';
  1194.     c /= 8;
  1195.     octal[0] = c + '0';
  1196. }
  1197.  
  1198.  
  1199. /*
  1200.  * Given a character c, put the 2-digit ascii hex value of that char
  1201.  * in the 2nd argument, which must be at least 2 in length.
  1202.  */
  1203. void
  1204. char_to_hex_pair(c, hex)
  1205.     int   c;
  1206.     char *hex;
  1207. {
  1208.     int d;
  1209.  
  1210.     c &= 0xff;
  1211.     d = c % 16;
  1212.     hex[1] = (d < 10) ? ('0' + d) : ('a' + d - 10);
  1213.     c /= 16;
  1214.     hex[0] = (c < 10) ? ('0' + c) : ('a' + c - 10);
  1215. }
  1216.  
  1217.  
  1218. /*
  1219.  * Convert in memory string s to a C-style string, with backslash escapes
  1220.  * like they're used in C character constants.
  1221.  *
  1222.  * Returns allocated C string version of s.
  1223.  */
  1224. char *
  1225. string_to_cstring(s)
  1226.     char *s;
  1227. {
  1228.     char *b, *p;
  1229.     int   n, i;
  1230.  
  1231.     if(!s)
  1232.       return(cpystr(""));
  1233.  
  1234.     n = 20;
  1235.     b = (char *)fs_get((n+1) * sizeof(char));
  1236.     p  = b;
  1237.     *p = '\0';
  1238.     i  = 0;
  1239.  
  1240.     while(*s){
  1241.     if(i + 4 > n){
  1242.         /*
  1243.          * The output string may overflow the output buffer.
  1244.          * Make more room.
  1245.          */
  1246.         n += 20;
  1247.         fs_resize((void **)&b, (n+1) * sizeof(char));
  1248.         p = &b[i];
  1249.     }
  1250.     else{
  1251.         switch(*s){
  1252.           case '\n':
  1253.         *p++ = '\\';
  1254.         *p++ = 'n';
  1255.         i += 2;
  1256.         break;
  1257.  
  1258.           case '\r':
  1259.         *p++ = '\\';
  1260.         *p++ = 'r';
  1261.         i += 2;
  1262.         break;
  1263.  
  1264.           case '\t':
  1265.         *p++ = '\\';
  1266.         *p++ = 't';
  1267.         i += 2;
  1268.         break;
  1269.  
  1270.           case '\b':
  1271.         *p++ = '\\';
  1272.         *p++ = 'b';
  1273.         i += 2;
  1274.         break;
  1275.  
  1276.           case '\f':
  1277.         *p++ = '\\';
  1278.         *p++ = 'f';
  1279.         i += 2;
  1280.         break;
  1281.  
  1282.           case '\\':
  1283.         *p++ = '\\';
  1284.         *p++ = '\\';
  1285.         i += 2;
  1286.         break;
  1287.  
  1288.           default:
  1289.         if(*s >= SPACE && *s <= '~'){
  1290.             *p++ = *s;
  1291.             i++;
  1292.         }
  1293.         else{  /* use octal output */
  1294.             *p++ = '\\';
  1295.             char_to_octal_triple(*s, p);
  1296.             p += 3;
  1297.             i += 4;
  1298.         }
  1299.  
  1300.         break;
  1301.         }
  1302.  
  1303.         s++;
  1304.     }
  1305.     }
  1306.  
  1307.     *p = '\0';
  1308.     return(b);
  1309. }
  1310.  
  1311.  
  1312. /*
  1313.  * Convert C-style string, with backslash escapes, into a hex string, two
  1314.  * hex digits per character.
  1315.  *
  1316.  * Returns allocated hexstring version of s.
  1317.  */
  1318. char *
  1319. cstring_to_hexstring(s)
  1320.     char *s;
  1321. {
  1322.     char *b, *p;
  1323.     int   n, i, c;
  1324.  
  1325.     if(!s)
  1326.       return(cpystr(""));
  1327.  
  1328.     n = 20;
  1329.     b = (char *)fs_get((n+1) * sizeof(char));
  1330.     p  = b;
  1331.     *p = '\0';
  1332.     i  = 0;
  1333.  
  1334.     while(*s){
  1335.     if(i + 2 > n){
  1336.         /*
  1337.          * The output string may overflow the output buffer.
  1338.          * Make more room.
  1339.          */
  1340.         n += 20;
  1341.         fs_resize((void **)&b, (n+1) * sizeof(char));
  1342.         p = &b[i];
  1343.     }
  1344.     else{
  1345.         if(*s == '\\'){
  1346.         s++;
  1347.         switch(*s){
  1348.           case 'n':
  1349.             c = '\n';
  1350.             char_to_hex_pair(c, p);
  1351.             i += 2;
  1352.             p += 2;
  1353.             s++;
  1354.             break;
  1355.  
  1356.           case 'r':
  1357.             c = '\r';
  1358.             char_to_hex_pair(c, p);
  1359.             i += 2;
  1360.             p += 2;
  1361.             s++;
  1362.             break;
  1363.  
  1364.           case 't':
  1365.             c = '\t';
  1366.             char_to_hex_pair(c, p);
  1367.             i += 2;
  1368.             p += 2;
  1369.             s++;
  1370.             break;
  1371.  
  1372.           case 'v':
  1373.             c = '\v';
  1374.             char_to_hex_pair(c, p);
  1375.             i += 2;
  1376.             p += 2;
  1377.             s++;
  1378.             break;
  1379.  
  1380.           case 'b':
  1381.             c = '\b';
  1382.             char_to_hex_pair(c, p);
  1383.             i += 2;
  1384.             p += 2;
  1385.             s++;
  1386.             break;
  1387.  
  1388.           case 'f':
  1389.             c = '\f';
  1390.             char_to_hex_pair(c, p);
  1391.             i += 2;
  1392.             p += 2;
  1393.             s++;
  1394.             break;
  1395.  
  1396.           case 'a':
  1397.             c = '\007';
  1398.             char_to_hex_pair(c, p);
  1399.             i += 2;
  1400.             p += 2;
  1401.             s++;
  1402.             break;
  1403.  
  1404.           case '\\':
  1405.             c = '\\';
  1406.             char_to_hex_pair(c, p);
  1407.             i += 2;
  1408.             p += 2;
  1409.             s++;
  1410.             break;
  1411.  
  1412.           case '?':
  1413.             c = '?';
  1414.             char_to_hex_pair(c, p);
  1415.             i += 2;
  1416.             p += 2;
  1417.             s++;
  1418.             break;
  1419.  
  1420.           case '\'':
  1421.             c = '\'';
  1422.             char_to_hex_pair(c, p);
  1423.             i += 2;
  1424.             p += 2;
  1425.             s++;
  1426.             break;
  1427.  
  1428.           case '\"':
  1429.             c = '\"';
  1430.             char_to_hex_pair(c, p);
  1431.             i += 2;
  1432.             p += 2;
  1433.             s++;
  1434.             break;
  1435.  
  1436.           case 0: /* reached end of s too early */
  1437.             c = 0;
  1438.             char_to_hex_pair(c, p);
  1439.             i += 2;
  1440.             p += 2;
  1441.             s++;
  1442.             break;
  1443.  
  1444.           /* hex number */
  1445.           case 'x':
  1446.             s++;
  1447.             if(isxdigit(*s)){
  1448.             c = read_hex(s);
  1449.             s++;
  1450.             if(isxdigit(*s))
  1451.               s++;
  1452.             }
  1453.             else
  1454.               c = 0;
  1455.  
  1456.             char_to_hex_pair(c, p);
  1457.             i += 2;
  1458.             p += 2;
  1459.  
  1460.             break;
  1461.  
  1462.           /* octal number */
  1463.           default:
  1464.             if(isdigit(*s)){
  1465.             c = read_octal(s);
  1466.             s++;
  1467.             if(isdigit(*s)){
  1468.                 s++;
  1469.                 if(isdigit(*s))
  1470.                   s++;
  1471.             }
  1472.             }
  1473.             else
  1474.               c = 0;
  1475.  
  1476.             char_to_hex_pair(c, p);
  1477.             i += 2;
  1478.             p += 2;
  1479.  
  1480.             break;
  1481.         }
  1482.         }
  1483.         else{
  1484.         char_to_hex_pair(*s, p);
  1485.         i += 2;
  1486.         p += 2;
  1487.         s++;
  1488.         }
  1489.     }
  1490.     }
  1491.  
  1492.     *p = '\0';
  1493.     return(b);
  1494. }
  1495.  
  1496.  
  1497. /*
  1498.  * Returns non-zero if dir is a prefix of path.
  1499.  *         zero     if dir is not a prefix of path, or if dir is empty.
  1500.  */
  1501. int
  1502. in_dir(dir, path)
  1503.     char *dir;
  1504.     char *path;
  1505. {
  1506.     return(*dir ? !strncmp(dir, path, strlen(dir)) : 0);
  1507. }
  1508.  
  1509.  
  1510.  
  1511. /*
  1512.  * Return pointer to given string after turning off all hi-order bits
  1513.  */
  1514. char *
  1515. bitstrip(s)
  1516.     char *s;
  1517. {
  1518.     register char *p = s;
  1519.  
  1520.     while(*p &= 0x7f)
  1521.       p++;
  1522.  
  1523.     return(s);
  1524. }
  1525.  
  1526.  
  1527.  
  1528. /*
  1529.  *  * * * * * * * *      RFC 1522 support routines      * * * * * * * *
  1530.  *
  1531.  *   RFC 1522 support is *very* loosely based on code contributed
  1532.  *   by Lars-Erik Johansson <lej@cdg.chalmers.se>.  Thanks to Lars-Erik,
  1533.  *   and appologies for taking such liberties with his code.
  1534.  */
  1535.  
  1536.  
  1537. #define    RFC1522_INIT    "=?"
  1538. #define    RFC1522_INIT_L    2
  1539. #define RFC1522_TERM    "?="
  1540. #define    RFC1522_TERM_L    2
  1541. #define    RFC1522_DLIM    "?"
  1542. #define    RFC1522_DLIM_L    1
  1543. #define    RFC1522_MAXW    75
  1544. #define    ESPECIALS    "()<>@,;:\"/[]?.="
  1545. #define    RFC1522_OVERHEAD(S)    (RFC1522_INIT_L + RFC1522_TERM_L +    \
  1546.                  (2 * RFC1522_DLIM_L) + strlen(S) + 1);
  1547. #define    RFC1522_ENC_CHAR(C)    (((C) & 0x80) || !rfc1522_valtok(C)    \
  1548.                  || (C) == '_' )
  1549.  
  1550.  
  1551. int           rfc1522_token PROTO((char *, int (*) PROTO((int)), char *,
  1552.                     char **));
  1553. int           rfc1522_valtok PROTO((int));
  1554. int           rfc1522_valenc PROTO((int));
  1555. int           rfc1522_valid PROTO((char *, char **, char **, char **,
  1556.                     char **));
  1557. char          *rfc1522_8bit PROTO((void *, int));
  1558. char          *rfc1522_binary PROTO((void *, int));
  1559. unsigned char *rfc1522_encoded_word PROTO((unsigned char *, int, char *));
  1560.  
  1561.  
  1562. /*
  1563.  * rfc1522_decode - decode the given source string ala RFC 1522,
  1564.  *            IF NECESSARY, into the given destination buffer.
  1565.  *            Don't bother copying if it turns out decoding
  1566.  *            isn't necessary.
  1567.  *
  1568.  * Returns: pointer to either the destination buffer containing the
  1569.  *        decoded text, or a pointer to the source buffer if there was
  1570.  *        no reason to decode it.
  1571.  */
  1572. unsigned char *
  1573. rfc1522_decode(d, s, charset)
  1574.     unsigned char  *d;
  1575.     char       *s;
  1576.     char      **charset;
  1577. {
  1578.     unsigned char *rv = NULL, *p;
  1579.     char      *start = s, *sw, *cset, *enc, *txt, *ew, **q;
  1580.     unsigned long  l;
  1581.     int           i;
  1582.  
  1583.     *d = '\0';                    /* init destination */
  1584.     if(charset)
  1585.       *charset = NULL;
  1586.  
  1587.     while(s && (sw = strstr(s, RFC1522_INIT))){
  1588.     if(!rv)
  1589.       rv = d;                /* remember start of dest */
  1590.  
  1591.     /* validate the rest of the encoded-word */
  1592.     if(rfc1522_valid(sw, &cset, &enc, &txt, &ew)){
  1593.         /* copy everything between s and sw to destination */
  1594.         for(i = 0; &s[i] < sw; i++)
  1595.           if(!isspace(s[i])){ /* if some non-whitespace */
  1596.           while(s < sw)
  1597.             *d++ = (unsigned char) *s++;
  1598.  
  1599.           break;
  1600.           }
  1601.  
  1602.         enc[-1] = txt[-1] = ew[0] = '\0';    /* tie off token strings */
  1603.  
  1604.         /* Insert text explaining charset if we don't know what it is */
  1605.         if((!ps_global->VAR_CHAR_SET
  1606.         || strucmp((char *) cset, ps_global->VAR_CHAR_SET))
  1607.            && strucmp((char *) cset, "US-ASCII")){
  1608.         dprint(5, (debugfile, "RFC1522_decode: charset mismatch: %s\n",
  1609.                cset));
  1610.         if(charset){
  1611.             if(!*charset)        /* only write first charset */
  1612.               *charset = cpystr(cset);
  1613.         }
  1614.         else{
  1615.             *d++ = '[';
  1616.             sstrcpy((char **) &d, cset);
  1617.             *d++ = ']';
  1618.             *d++ = SPACE;
  1619.         }
  1620.         }
  1621.  
  1622.         /* based on encoding, write the encoded text to output buffer */
  1623.         switch(*enc){
  1624.           case 'Q' :            /* 'Q' encoding */
  1625.           case 'q' :
  1626.         /* special hocus-pocus to deal with '_' exception, too bad */
  1627.         for(l = 0L, i = 0; txt[l]; l++)
  1628.           if(txt[l] == '_')
  1629.             i++;
  1630.  
  1631.         if(i){
  1632.             q = (char **) fs_get((i + 1) * sizeof(char *));
  1633.             for(l = 0L, i = 0; txt[l]; l++)
  1634.               if(txt[l] == '_'){
  1635.               q[i++] = &txt[l];
  1636.               txt[l] = SPACE;
  1637.               }
  1638.  
  1639.             q[i] = NULL;
  1640.         }
  1641.         else
  1642.           q = NULL;
  1643.  
  1644.         if(p = rfc822_qprint((unsigned char *)txt, strlen(txt), &l)){
  1645.             strcpy((char *) d, (char *) p);
  1646.             fs_give((void **)&p);    /* free encoded buf */
  1647.             d += l;            /* advance dest ptr to EOL */
  1648.         }
  1649.         else
  1650.           goto bogus;
  1651.  
  1652.         if(q){                /* restore underscores */
  1653.             for(i = 0; q[i]; i++)
  1654.               *(q[i]) = '_';
  1655.  
  1656.             fs_give((void **)&q);
  1657.         }
  1658.  
  1659.         break;
  1660.  
  1661.           case 'B' :            /* 'B' encoding */
  1662.           case 'b' :
  1663.         if(p = rfc822_base64((unsigned char *) txt, strlen(txt), &l)){
  1664.             strcpy((char *) d, (char *) p);
  1665.             fs_give((void **)&p);    /* free encoded buf */
  1666.             d += l;            /* advance dest ptr to EOL */
  1667.         }
  1668.         else
  1669.           goto bogus;
  1670.  
  1671.         break;
  1672.  
  1673.           default:
  1674.         sstrcpy((char **) &d, txt);
  1675.         dprint(1, (debugfile, "RFC1522_decode: Unknown ENCODING: %s\n",
  1676.                enc));
  1677.         break;
  1678.         }
  1679.  
  1680.         /* restore trompled source string */
  1681.         enc[-1] = txt[-1] = '?';
  1682.         ew[0]   = RFC1522_TERM[0];
  1683.  
  1684.         /* advance s to start of text after encoded-word */
  1685.         s = ew + RFC1522_TERM_L;
  1686.     }
  1687.     else{
  1688.         /* found intro, but bogus data followed */
  1689.         strncpy((char *) d, s, (int) (l = (sw - s) + RFC1522_INIT_L));
  1690.         *(d += l) = '\0';            /* advance d, tie off text */
  1691.         s += l;                /* advance s beyond intro */
  1692.     }
  1693.     }
  1694.  
  1695.     if(rv && *s)                /* copy remaining text */
  1696.       strcat((char *) rv, s);
  1697.  
  1698. /* BUG: MUST do code page mapping under DOS after decoding */
  1699.  
  1700.     return(rv ? rv : (unsigned char *) start);
  1701.  
  1702.   bogus:
  1703.     dprint(1, (debugfile, "RFC1522_decode: BOGUS INPUT: -->%s<--\n", start));
  1704.     return((unsigned char *) start);
  1705. }
  1706.  
  1707.  
  1708. /*
  1709.  * rfc1522_token - scan the given source line up to the end_str making
  1710.  *           sure all subsequent chars are "valid" leaving endp
  1711.  *           a the start of the end_str.
  1712.  * Returns: TRUE if we got a valid token, FALSE otherwise
  1713.  */
  1714. int
  1715. rfc1522_token(s, valid, end_str, endp)
  1716.     char  *s;
  1717.     int     (*valid) PROTO((int));
  1718.     char  *end_str;
  1719.     char **endp;
  1720. {
  1721.     while(*s){
  1722.     if((char) *s == *end_str        /* test for matching end_str */
  1723.        && ((end_str[1])
  1724.             ? !strncmp((char *)s + 1, end_str + 1, strlen(end_str + 1))
  1725.             : 1)){
  1726.         *endp = s;
  1727.         return(TRUE);
  1728.     }
  1729.  
  1730.     if(!(*valid)(*s++))            /* test for valid char */
  1731.       break;
  1732.     }
  1733.  
  1734.     return(FALSE);
  1735. }
  1736.  
  1737.  
  1738. /*
  1739.  * rfc1522_valtok - test for valid character in the RFC 1522 encoded
  1740.  *            word's charset and encoding fields.
  1741.  */
  1742. int
  1743. rfc1522_valtok(c)
  1744.     int c;
  1745. {
  1746.     return(!(c == SPACE || iscntrl(c & 0x7f) || strindex(ESPECIALS, c)));
  1747. }
  1748.  
  1749.  
  1750. /*
  1751.  * rfc1522_valenc - test for valid character in the RFC 1522 encoded
  1752.  *            word's encoded-text field.
  1753.  */
  1754. int
  1755. rfc1522_valenc(c)
  1756.     int c;
  1757. {
  1758.     return(!(c == '?' || c == SPACE) && isprint(c));
  1759. }
  1760.  
  1761.  
  1762. /*
  1763.  * rfc1522_valid - validate the given string as to it's rfc1522-ness
  1764.  */
  1765. int
  1766. rfc1522_valid(s, charset, enc, txt, endp)
  1767.     char  *s;
  1768.     char **charset;
  1769.     char **enc;
  1770.     char **txt;
  1771.     char **endp;
  1772. {
  1773.     char *c, *e, *t, *p;
  1774.     int   rv;
  1775.  
  1776.     rv = rfc1522_token(c = s+RFC1522_INIT_L, rfc1522_valtok, RFC1522_DLIM, &e)
  1777.        && rfc1522_token(++e, rfc1522_valtok, RFC1522_DLIM, &t)
  1778.        && rfc1522_token(++t, rfc1522_valenc, RFC1522_TERM, &p)
  1779.        && p - s <= RFC1522_MAXW;
  1780.  
  1781.     if(charset)
  1782.       *charset = c;
  1783.  
  1784.     if(enc)
  1785.       *enc = e;
  1786.  
  1787.     if(txt)
  1788.       *txt = t;
  1789.  
  1790.     if(endp)
  1791.       *endp = p;
  1792.  
  1793.     return(rv);
  1794. }
  1795.  
  1796.  
  1797. /*
  1798.  * rfc1522_encode - encode the given source string ala RFC 1522,
  1799.  *            IF NECESSARY, into the given destination buffer.
  1800.  *            Don't bother copying if it turns out encoding
  1801.  *            isn't necessary.
  1802.  *
  1803.  * Returns: pointer to either the destination buffer containing the
  1804.  *        encoded text, or a pointer to the source buffer if we didn't
  1805.  *          have to encode anything.
  1806.  */
  1807. char *
  1808. rfc1522_encode(d, s, charset)
  1809.     char      *d;
  1810.     unsigned char *s;
  1811.     char      *charset;
  1812. {
  1813.     unsigned char *p, *q;
  1814.     char       enc;
  1815.     int           n, l;
  1816.  
  1817.     if(!(s && charset))
  1818.       return((char *) s);
  1819.  
  1820.     /* look for a reason to encode */
  1821.     for(p = s, n = 0; *p; p++)
  1822.       if((*p) & 0x80){
  1823.       n++;
  1824.       }
  1825.       else if(*p == RFC1522_INIT[0]
  1826.           && !strncmp((char *) p, RFC1522_INIT, RFC1522_INIT_L)){
  1827.       if(rfc1522_valid((char *) p, NULL, NULL, NULL, (char **) &q))
  1828.         p = q + RFC1522_TERM_L - 1;        /* advance past encoded gunk */
  1829.       else
  1830.         n++;
  1831.       }
  1832.       else if(*p == ESCAPE && match_escapes((char *)(p+1))){
  1833.       n++;
  1834.       }
  1835.  
  1836.     if(n){                    /* found, encoding to do */
  1837.     char *rv  = d, *t,
  1838.           enc = (n > (2 * (p - s)) / 3) ? 'B' : 'Q';
  1839.     int   slen;
  1840.  
  1841.     while(*s){
  1842.         sstrcpy(&d, RFC1522_INIT);        /* insert intro header, */
  1843.         sstrcpy(&d, charset);        /* character set tag, */
  1844.         sstrcpy(&d, RFC1522_DLIM);        /* and encoding flavor */
  1845.         *d++ = enc;
  1846.         sstrcpy(&d, RFC1522_DLIM);
  1847.  
  1848.         /*
  1849.          * feed lines to encoder such that they're guaranteed
  1850.          * less than RFC1522_MAXW.
  1851.          */
  1852.         p = rfc1522_encoded_word(s, enc, charset);
  1853.         if(enc == 'B')            /* insert encoded data */
  1854.           sstrcpy(&d, t = rfc1522_binary(s, p - s));
  1855.         else                /* 'Q' encoding */
  1856.           sstrcpy(&d, t = rfc1522_8bit(s, p - s));
  1857.  
  1858.         sstrcpy(&d, RFC1522_TERM);        /* insert terminator */
  1859.         fs_give((void **) &t);
  1860.         if(*p)                /* more src string follows */
  1861.           sstrcpy(&d, "\015\012 ");    /* insert continuation line */
  1862.  
  1863.         s = p;                /* advance s */
  1864.     }
  1865.  
  1866.     return(rv);
  1867.     }
  1868.     else
  1869.       return((char *) s);            /* no work for us here */
  1870. }
  1871.  
  1872.  
  1873.  
  1874. /*
  1875.  * rfc1522_encoded_word -- cut given string into max length encoded word
  1876.  *
  1877.  * Return: pointer into 's' such that the encoded 's' is no greater
  1878.  *       than RFC1522_MAXW
  1879.  *
  1880.  *  NOTE: this line break code is NOT cognizant of any SI/SO
  1881.  *  charset requirements nor similar strategies using escape
  1882.  *  codes.  Hopefully this will matter little and such
  1883.  *  representation strategies don't also include 8bit chars.
  1884.  */
  1885. unsigned char *
  1886. rfc1522_encoded_word(s, enc, charset)
  1887.     unsigned char *s;
  1888.     int           enc;
  1889.     char      *charset;
  1890. {
  1891.     int goal = RFC1522_MAXW - RFC1522_OVERHEAD(charset);
  1892.  
  1893.     if(enc == 'B')            /* base64 encode */
  1894.       for(goal = ((goal / 4) * 3) - 2; goal && *s; goal--, s++)
  1895.     ;
  1896.     else                /* special 'Q' encoding */
  1897.       for(; goal && *s; s++)
  1898.     if((goal -= RFC1522_ENC_CHAR(*s) ? 3 : 1) < 0)
  1899.       break;
  1900.  
  1901.     return(s);
  1902. }
  1903.  
  1904.  
  1905.  
  1906. /*
  1907.  * rfc1522_8bit -- apply RFC 1522 'Q' encoding to the given 8bit buffer
  1908.  *
  1909.  * Return: alloc'd buffer containing encoded string
  1910.  */
  1911. char *
  1912. rfc1522_8bit(src, slen)
  1913.     void *src;
  1914.     int   slen;
  1915. {
  1916.     static char *hex = "0123456789ABCDEF";
  1917.     char *ret = (char *) fs_get ((size_t) (3*slen + 2));
  1918.     char *d = ret;
  1919.     unsigned char c;
  1920.     unsigned char *s = (unsigned char *) src;
  1921.  
  1922.     while (slen--) {                /* for each character */
  1923.     if (((c = *s++) == '\015') && (*s == '\012') && slen) {
  1924.         *d++ = '\015';            /* true line break */
  1925.         *d++ = *s++;
  1926.         slen--;
  1927.     }
  1928.     else if(c == SPACE){            /* special encoding case */
  1929.         *d++ = '_';
  1930.     }
  1931.     else if(RFC1522_ENC_CHAR(c)){
  1932.         *d++ = '=';                /* quote character */
  1933.         *d++ = hex[c >> 4];            /* high order 4 bits */
  1934.         *d++ = hex[c & 0xf];        /* low order 4 bits */
  1935.     }
  1936.     else
  1937.       *d++ = (char) c;            /* ordinary character */
  1938.     }
  1939.  
  1940.     *d = '\0';                    /* tie off destination */
  1941.     return(ret);
  1942. }
  1943.  
  1944.  
  1945. /*
  1946.  * rfc1522_binary -- apply RFC 1522 'B' encoding to the given 8bit buffer
  1947.  *
  1948.  * Return: alloc'd buffer containing encoded string
  1949.  */
  1950. char *
  1951. rfc1522_binary (src, srcl)
  1952.     void *src;
  1953.     int   srcl;
  1954. {
  1955.     static char *v =
  1956.             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  1957.     unsigned char *s = (unsigned char *) src;
  1958.     char *ret, *d;
  1959.  
  1960.     d = ret = (char *) fs_get ((size_t) ((((srcl + 2) / 3) * 4) + 1));
  1961.     for (; srcl; s += 3) {    /* process tuplets */
  1962.                 /* byte 1: high 6 bits (1) */
  1963.     *d++ = v[s[0] >> 2];
  1964.                 /* byte 2: low 2 bits (1), high 4 bits (2) */
  1965.     *d++ = v[((s[0] << 4) + (--srcl ? (s[1] >> 4) : 0)) & 0x3f];
  1966.                 /* byte 3: low 4 bits (2), high 2 bits (3) */
  1967.     *d++ = srcl ? v[((s[1] << 2) + (--srcl ? (s[2] >> 6) :0)) & 0x3f] :'=';
  1968.                 /* byte 4: low 6 bits (3) */
  1969.     *d++ = srcl ? v[s[2] & 0x3f] : '=';
  1970.     if(srcl)
  1971.       srcl--;        /* count third character if processed */
  1972.     }
  1973.  
  1974.     *d = '\0';            /* tie off string */
  1975.     return(ret);        /* return the resulting string */
  1976. }
  1977.